# python test_worker_main.py
import os
import re
import signal
import subprocess
import time

import select

NUM_OF_SUB_WORKER = 5
SUB_WORKER_NAME = "test_sub_worker_plus"

def readline_with_timeout(process, timeout):
    poll_obj = select.poll()
    poll_obj.register(process.stdout, select.POLLIN)

    ready = poll_obj.poll(timeout)
    if len(ready) > 0:
        return process.stdout.readline()
    else:
        return None

class SubWorker():
    worker_home = {}
    pattern = re.compile(r"fifo_sent (\w+) to worker (\d+) by (\w+)")

    def __init__(self, worker_path, worker_id, work_path):
        self.worker_path = worker_path
        self.worker_id = worker_id
        self.work_path = work_path
    def start_worker(self):
        self.process = subprocess.Popen([self.worker_path, f'{self.worker_id}'],
                                        stdin=subprocess.PIPE,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        text=True,
                                        cwd=self.work_path)
        if not self.process:
            print(f"{self.worker_id} 启动失败")
            return 0
        output = self.process.stdout.readline()
        print(f"Output from sub_worker {self.worker_id}: {output}")
        print(f"sub_worker {self.worker_id} 启动成功")
        SubWorker.worker_home[self.worker_id] = self
    def send_one_commend(self, do_what):
        print(f"worker {self.worker_id} do {do_what}", end='')
        self.process.stdin.write(do_what)
        self.process.stdin.flush()

    def show_one_line_output(self):
        # output = processes[who_do].stdout.readline()
        output = readline_with_timeout(self.process, 1000)
        print(f"Output from sub_worker {self.worker_id}: {output}", end='')
        if output[-1] != '\n':
            print()

    def do_simple_and_get_return(self, do_what):
        self.send_one_commend(do_what)
        self.show_one_line_output()

    def do_one_and_get_return(self, do_what):
        # 判断是否为简单命令
        if "fifo_sent" in do_what:
            print(f"worker {self.worker_id} do {do_what}", end='')
            match = SubWorker.pattern.match(do_what)
            if match:
                sent_what = match.group(1).strip()
                to_who = int(match.group(2))
                by_what = match.group(3).strip()

                self.send_one_commend(f"fifo_write {by_what} {sent_what}\n")
                SubWorker.worker_home[to_who].send_one_commend(f"fifo_read {by_what}\n")

                self.show_one_line_output()
                SubWorker.worker_home[to_who].show_one_line_output()

        else:    #是简单命令
            self.do_simple_and_get_return(do_what)

    def close(self):
        try:
            p_output, p_error = self.process.communicate("exit\n", timeout=10)
            if p_output:
                print(f"Last output from sub_worker {self.worker_id}: {p_output}")
            if p_error:
                print(f"Last p_error from sub_worker {self.worker_id}: {p_error}")
            print(f"{self.worker_id} is closed")

            self.process.wait(timeout=1)
        except :
            print(f"sub_worker {self.worker_id} 运行超时")

            try:
                os.killpg(self.process.pid, signal.SIGTERM)
            except :
                pass


def start_working(action_plan="test_point0.txt",
                  worker_path_list=(f'./{SUB_WORKER_NAME}' for _ in range(NUM_OF_SUB_WORKER)),
                  worker_working_dir = '.'
                  ):
    print("{:*^66}".format(" 启动 sub_worker "))
    # 启动多个C语言 test_sub_worker_plus 的子进程
    workers = []
    for i, i_worker in enumerate(worker_path_list):
        workers.append(SubWorker(i_worker, i, worker_working_dir))

    for i in workers:
        i.start_worker()
    pattern = re.compile(r"^worker ([0-9]\d*) do (.*)$")
    print("{:*^66}".format(" 测试开始 "))
    with open(action_plan, "r") as f:
        while True:
            # Get next line from file
            line = f.readline()
            # If line is empty then end of file reached
            if not line:
                break
            # print(line.strip())
            match = pattern.match(line)
            if match:
                who_do = int(match.group(1))
                do_what_raw = match.group(2)
                if "#" in do_what_raw:
                    do_what_raw = do_what_raw[:do_what_raw.find('#')]
                if len(do_what_raw) == 0:
                    continue
                do_what = do_what_raw + '\n'
                workers[who_do].do_one_and_get_return(do_what)
                time.sleep(5)

    print("{:*^66}".format(" 命令运行完毕 "))

    # 等待所有子进程结束
    for i in workers:
        i.close()

if __name__ == "__main__":
    start_working()